import { VueConstructor, VueBase,OptionsContext,VueMixin,Vue } from "./vue"
import { ComponentOptions} from "vue"
import { isFunction } from '@gowiny/js-utils'

export function getOptionsByClass(Ctor:any){
    let options
    if(Ctor.__vccOpts){
        options = Ctor.__vccOpts
    }else{
        options = Ctor
    }
    return options;
}

export function toEmitMap(val:any){
    if(!val || !Array.isArray(val)){
        return val;
    }else{
        let rs:Record<string,any> = {};
        val.forEach(item=>{
            rs[item] = null;
        })
        return rs;
    }
}
export function mergeSetup(...args:any[]){
    if(!args){
        return
    }else if(args.length == 1){
        return args[0]
    }else{
        const result:any = {}
        args.forEach((item)=>{
            if(!item){
                return
            }else if(isFunction(item)){
                result.render = item
            }else{
                Object.assign(result,item)
            }
        })
        return result
    }
}

export interface VueDecorator {
    // Class decorator
    (Ctor: VueConstructor<VueBase>): void
    // Property decorator
    (target: VueBase, key: string): void
    // Parameter decorator
    (target: VueBase, key: string, index: number): void
}

export function initDecorator(Ctor:any){
    if(!Ctor.__d) {
        Ctor.__d = []
        Ctor.__d.__ctor = Ctor
    }else if(Ctor.__d.__ctor !== Ctor){
        Ctor.__d = [].concat(Ctor.__d)
        Ctor.__d.__ctor = Ctor
    }
}

export function createDecorator(
    factory: (ctx : OptionsContext, key: string, index: number) => void
    ): VueDecorator {
    return (
        target: VueBase | VueConstructor<VueBase>,
        key?: any,
        index?: any
    ) => {
        const Ctor:any =
        typeof target === 'function'
            ? target
            : (target.constructor as VueConstructor)

        initDecorator(Ctor)
        if (typeof index !== 'number') {
        index = undefined
        }
        Ctor.__d.push((ctx:OptionsContext) => factory(ctx, key, index))
    }
}

export type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never

export type ExtractInstance<T> = T extends VueMixin<infer V> ? V : never


export type MixedVueBase<Mixins extends VueMixin[]> = Mixins extends (infer T)[] ? VueConstructor<UnionToIntersection<ExtractInstance<T>> & Vue> : never

export function mixins<T extends VueMixin[]>(...Ctors: T): MixedVueBase<T>
export function mixins(...Ctors: VueMixin[]): VueConstructor {
    return class MixedVue extends Vue {
    static __b: ComponentOptions = {
        mixins: Ctors.map((Ctor) => Ctor.__vccOpts),
    }

    constructor(...args: any[]) {
        super(...args)
        Ctors.forEach((Ctor) => {
            const data = new (Ctor as VueConstructor)(...args)
            Object.keys(data).forEach((key) => {
                ;(this as any)[key] = (data as any)[key]
            })
        })
    }
    }
}
